#include "stdlib.hpp"
#include <purewell/purewell.hpp>
#include <purewell/buffer.hpp>

#include "buffersimple.hpp"

namespace purewell
{

PWBufferSimple::PWBufferSimple() : m_buf(NULL), m_blen(0)
{
}

PWBufferSimple::~PWBufferSimple()
{
	if ( m_buf )
	{
		free(m_buf);
		m_buf = NULL;
	}
}

ssize_t
PWBufferSimple::read(void* buf, size_t blen) const
{
	const size_t rlen(std::min(blen,m_blen));
	memcpy(buf, m_buf, rlen);
	return (ssize_t)rlen;
}

ssize_t
PWBufferSimple::readAndSkip(void* buf, size_t blen)
{
	const size_t rlen(std::min(blen,m_blen));
	memcpy(buf, m_buf, rlen);

	if ( !_shrink(rlen) ) return (ssize_t)-1;

	return (ssize_t)rlen;
}

ssize_t
PWBufferSimple::skip(size_t blen)
{
	const size_t rlen(std::min(blen,m_blen));
	if ( !_shrink(rlen) ) return (ssize_t)-1;

	return (ssize_t)rlen;
}

ssize_t
PWBufferSimple::write(const void* buf, size_t blen)
{
	const size_t oblen(m_blen);

	if ( !_increase(blen) )
	{
		return (ssize_t)-1;
	}

	memcpy(m_buf+oblen, buf, blen);

	return (ssize_t)blen;
}

bool
PWBufferSimple::_increase(size_t delta)
{
	const size_t blen(m_blen+delta);
	char* ptr((char*)malloc(blen));
	if ( !ptr ) return false;

	if ( m_buf )
	{
		memcpy(ptr, m_buf, m_blen);
		free(m_buf);
	}

	m_blen = blen;
	m_buf = ptr;

	return true;
}

bool
PWBufferSimple::_shrink(size_t delta)
{
	size_t blen (0);
	if ( delta < m_blen ) blen = m_blen - delta;

	if ( 0 == blen )
	{
		if ( m_buf )
		{
			free(m_buf);
			m_buf = NULL;
		}

		m_blen = 0;
		return true;
	}

	char* ptr((char*)malloc(blen));
	if ( !ptr ) return false;
	memcpy(ptr, m_buf+delta, blen);
	m_blen = blen;
	free(m_buf);
	m_buf = ptr;

	return true;
}

void
PWBufferSimple::clear(void)
{
	if ( m_buf )
	{
		free(m_buf);
		m_buf = NULL;
	}

	m_blen = 0;
}

ssize_t
PWBufferSimple::readLine(void* _buf, size_t blen) const
{
	char* buf((char*)_buf);
	const char* ie(strchr(m_buf, '\n'));
	if ( NULL == ie ) return -1;
	const size_t linelen(m_buf-ie+1);

	strncpy(buf, m_buf, std::min(blen, linelen));
	return linelen;
}

ssize_t
PWBufferSimple::readLineAndSkip(void* _buf, size_t blen)
{
	char* buf((char*)_buf);
	const char* ie(strchr(m_buf, '\n'));
	if ( NULL == ie ) return -1;
	const size_t linelen(m_buf-ie+1);
	const size_t rlen(std::min(blen, linelen));

	strncpy(buf, m_buf, rlen);
	skip(rlen);

	return linelen;
}

};//namespace purewell


